#include "djpeg_intf.h"


//////////////////////////////////////////////////////////////////////////

/* type definition */

typedef struct _JPEGSRCMGR_
{
	struct jpeg_source_mgr pub;	/* public fields */

#if 0   // yuyiwei
	FILE * infile;		/* source stream */
#endif
	JOCTET * buffer;		/* start of buffer */
	boolean start_of_file;	/* have we gotten any data yet? */
} JPEGSRCMGR;

typedef struct _JPEGDEST_
{
	struct djpeg_dest_struct	pub;
} JPEGDEST;

/* static variables */

static const char * const cdjpeg_message_table[] = {
#include "cderror.h"
	NULL
};

/* static methods */

/* .. common routines */

/*
* Marker processor for COM and interesting APPn markers.
* This replaces the library's built-in processor, which just skips the marker.
* We want to print out the marker as text, to the extent possible.
* Note this code relies on a non-suspending data source.
*/

LOCAL(unsigned int)
jpeg_getc (j_decompress_ptr cinfo)
/* Read next byte */
{
	struct jpeg_source_mgr * datasrc = cinfo->src;

	if (datasrc->bytes_in_buffer == 0) {
		if (! (*datasrc->fill_input_buffer) (cinfo))
			ERREXIT(cinfo, JERR_CANT_SUSPEND);
	}
	datasrc->bytes_in_buffer--;
	return GETJOCTET(*datasrc->next_input_byte++);
}

METHODDEF(boolean)
print_text_marker (j_decompress_ptr pInfo)
{
	boolean traceit = (pInfo->err->trace_level >= 1);
	INT32 length;
	unsigned int ch;
	unsigned int lastch = 0;

	length = jpeg_getc(pInfo) << 8;
	length += jpeg_getc(pInfo);
	length -= 2;			/* discount the length word itself */

#if 0   // yuyiwei
	if (traceit) {
		if (pInfo->unread_marker == JPEG_COM)
			fprintf(stderr, "Comment, length %ld:\n", (long) length);
		else			/* assume it is an APPn otherwise */
			fprintf(stderr, "APP%d, length %ld:\n",
			pInfo->unread_marker - JPEG_APP0, (long) length);
	}
#endif

	while (--length >= 0) {
		ch = jpeg_getc(pInfo);
#if 0
		if (traceit) {
			/* Emit the character in a readable form.
			* Nonprintables are converted to \nnn form,
			* while \ is converted to \\.
			* Newlines in CR, CR/LF, or LF form will be printed as one newline.
			*/
			if (ch == '\r') {
				fprintf(stderr, "\n");
			} else if (ch == '\n') {
				if (lastch != '\r')
					fprintf(stderr, "\n");
			} else if (ch == '\\') {
				fprintf(stderr, "\\\\");
			} else if (isprint(ch)) {
				putc(ch, stderr);
			} else {
				fprintf(stderr, "\\%03o", ch);
			}
			lastch = ch;
		}
#endif
	}

#if 0   // yuyiwei
	if (traceit)
		fprintf(stderr, "\n");
#endif

	return TRUE;
}

/* .. input related */
METHODDEF(void)
init_source (j_decompress_ptr pInfo)
{
	JPEGSRCMGR *pSrc = (JPEGSRCMGR *) pInfo->src;

	/* We reset the empty-input-file flag for each image,
	* but we don't clear the input buffer.
	* This is correct behavior for reading a series of images from one source.
	*/
	pSrc->start_of_file = TRUE;
}

METHODDEF(boolean)
fill_input_buffer (j_decompress_ptr pInfo)
{
	// do nothing
	return TRUE;
}

METHODDEF(void)
term_source (j_decompress_ptr pInfo)
{
	/* no work necessary here */
}

METHODDEF(void)
skip_input_data (j_decompress_ptr pInfo, long num_bytes)
{
	JPEGSRCMGR * src = (JPEGSRCMGR *) pInfo->src;

	/* Just a dumb implementation for now.  Could use fseek() except
	* it doesn't work on pipes.  Not clear that being smart is worth
	* any trouble anyway --- large skips are infrequent.
	*/
	if (num_bytes > 0) {
		while (num_bytes > (long) src->pub.bytes_in_buffer) {
			num_bytes -= (long) src->pub.bytes_in_buffer;
			(void) fill_input_buffer(pInfo);
			/* note we assume that fill_input_buffer will never return FALSE,
			* so suspension need not be handled.
			*/
		}
		src->pub.next_input_byte += (size_t) num_bytes;
		src->pub.bytes_in_buffer -= (size_t) num_bytes;
	}
}

/* .. input setup */

static void SetupInput(j_decompress_ptr pInfo, unsigned char *pStream, int nStreamLen)
{
	JPEGSRCMGR *pSrc;

	if (pInfo->src == NULL) {	/* first time for this JPEG object? */
		pInfo->src = (struct jpeg_source_mgr *)
			(*pInfo->mem->alloc_small) ((j_common_ptr) pInfo, JPOOL_PERMANENT,
			SIZEOF(JPEGSRCMGR));
		pSrc = (JPEGSRCMGR *) pInfo->src;
		pSrc->buffer = (JOCTET *)pStream;
	}

	pSrc = (JPEGSRCMGR *) pInfo->src;
	pSrc->pub.init_source = init_source;
	pSrc->pub.fill_input_buffer = fill_input_buffer;
	pSrc->pub.skip_input_data = skip_input_data;
	pSrc->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
	pSrc->pub.term_source = term_source;
#if 0   // yuyiwei
	pSrc->infile = 0;
#endif

	pSrc->pub.next_input_byte = pSrc->buffer;
	pSrc->pub.bytes_in_buffer = nStreamLen;
	pSrc->start_of_file = FALSE;
}


//////////////////////////////////////////////////////////////////////////

/* public methods */

int jpegdec_Init(JPEGDEC *pJpegDec)
{
	int nRes = JPEG_ERR_NOERR;
	int nStructSize = sizeof(*pJpegDec);
	memset(pJpegDec, 0, nStructSize);

	/* Initialize the JPEG decompression object with default error handling. */
	pJpegDec->stInfo.err = jpeg_std_error(&pJpegDec->stErr);
	jpeg_create_decompress(&pJpegDec->stInfo);

	/* Add some application-specific error messages (from cderror.h) */
	pJpegDec->stErr.addon_message_table = cdjpeg_message_table;
	pJpegDec->stErr.first_addon_message = JMSG_FIRSTADDONCODE;
	pJpegDec->stErr.last_addon_message = JMSG_LASTADDONCODE;

	/* Insert custom marker processor for COM and APP12.
	 * APP12 is used by some digital camera makers for textual info,
	 * so we provide the ability to display it as text.
	 * If you like, additional APPn marker types can be selected for display,
	 * but don't try to override APP0 or APP14 this way (see libjpeg.doc).
	 */
	jpeg_set_marker_processor(&pJpegDec->stInfo, JPEG_COM, print_text_marker);
	jpeg_set_marker_processor(&pJpegDec->stInfo, JPEG_APP0+12, print_text_marker);

	// default
	pJpegDec->outclrspc = JCS_RGB;	// default output colorspace

	return nRes;
}

int jpegdec_Deinit(JPEGDEC *pJpegDec)
{
	int nRes = JPEG_ERR_NOERR;

	jpeg_destroy_decompress(&pJpegDec->stInfo);

	return nRes;
}

int jpegdec_Predecode(JPEGDEC *pJpegDec, unsigned char *pStream, int nStreamLen)
{
	int nRes = JPEG_ERR_NOERR;
	j_decompress_ptr pInfo = &pJpegDec->stInfo;

	SetupInput(pInfo, pStream, nStreamLen);

	/* Read file header, set default decompression parameters */
	(void) jpeg_read_header(&pJpegDec->stInfo, TRUE);

	pJpegDec->nImgHeight = pJpegDec->stInfo.image_height;
	pJpegDec->nImgWidth = pJpegDec->stInfo.image_width;

	return nRes;
}

int jpegdec_GetImageSize(JPEGDEC *pJpegDec, int *pWidth, int *pHeight)
{
	if (pWidth)
	{
		*pWidth = pJpegDec->nImgWidth;
	}
	if (pHeight)
	{
		*pHeight = pJpegDec->nImgHeight;
	}
	return JPEG_ERR_NOERR;
}

int jpegdec_SetOutputColorSpace(JPEGDEC *pJpegDec, J_COLOR_SPACE clrspc)
{
	// checking

	switch(clrspc)
	{
	case JCS_RGB:
	case JCS_YCbCr:
		break;
	default:
		return JPEG_ERR_PARAMNOWALLOWED;
	}

	pJpegDec->outclrspc = clrspc;

	return JPEG_ERR_NOERR;
}

int jpegdec_Decode(JPEGDEC *pJpegDec, unsigned char *pImage)
{
	unsigned char *pOutByte = pImage;
	JPEGDEST * pDestMgr = 0;
	j_decompress_ptr pInfo = &pJpegDec->stInfo;

	int nRowWidth;

	/* output setting */

	pDestMgr = (JPEGDEST *)
		(*pInfo->mem->alloc_small) ((j_common_ptr) pInfo, JPOOL_IMAGE,
		SIZEOF(JPEGDEST));

	// the component number of output will be calculated
	// accordingly in jpeg_calc_output_dimensions
	pInfo->out_color_space = pJpegDec->outclrspc;	
							
	jpeg_calc_output_dimensions(pInfo);
	if (pInfo->output_width != pInfo->image_width)
	{
		return JPEG_ERR_INCONSISTENCY;
	}

	nRowWidth = pInfo->output_width * pInfo->output_components;
	pDestMgr->pub.buffer = pInfo->mem->alloc_sarray
		((j_common_ptr) pInfo, JPOOL_IMAGE, nRowWidth, (JDIMENSION) 1);
	pDestMgr->pub.buffer_height = 1;


	/* Start decompressor */
	(void) jpeg_start_decompress(pInfo);


	/* decompressing */
	while (pJpegDec->stInfo.output_scanline < pJpegDec->stInfo.output_height) {
		//jpeg_read_scanlines (j_decompress_ptr pInfo, JSAMPARRAY scanlines, JDIMENSION max_lines)

		JDIMENSION nScanLines = jpeg_read_scanlines(&pJpegDec->stInfo, 
			pDestMgr->pub.buffer,
			pDestMgr->pub.buffer_height);
		memcpy(pOutByte, pDestMgr->pub.buffer[0], nRowWidth);
		pOutByte += nRowWidth;
	}

	/* finish decompressing */
	jpeg_finish_decompress(pInfo);

	return JPEG_ERR_NOERR;
}


